<?php
/*
 * Goals:
 *  - Combine multiple css or js files into one on the fly
 *  - Minify css or js files
 *  - Gzip css or js files
 *  - Ability to use with existing site by implementing mod_rewrite rule
 *  - Smart caching
 *      - Detect when new cache file needs to be generated based on
 *          - Contents of css or js file
 *          - Settings in php code
 *          - Changes to php code
 *  - Embed images in css files for fewer requests
 */

function minify($string){
    global $settings;

    // Compress whitespace.
    $string = preg_replace('/\s+/', ' ', $string);

    // Remove comments.
    $string = preg_replace('/\/\*.*?\*\//', '', $string);

    // Embed images in stylesheets
    if($settings['embed'] == TRUE){
        $string = preg_replace_callback("/url\(['|\"]([^('|\")]+)['|\"]\)/", 'convert_url', $string);
    }

    return trim($string);
}

function minifyJs($string) {
   	include_once('JSMin.php');
	return class_exists('JSMin') ? JSMin::minify($string) : $string;
}
function convert_url($url){
    global $settings;

    // Get full path name
      $file = realpath(dirname(__FILE__)).trim($url[1], '.');

    // If the file doesn't exist or is too large, return original string
    if(!file_exists($file) || filesize($file) > $settings['embed_max_size']){
        return $url;
    }

    // Get extension for setting mime type
    $extension = pathinfo($file,PATHINFO_EXTENSION);

    // Get file contents
    $file_contents = base64_encode(file_get_contents($file));

    return "url(data:image/$extension;base64,$file_contents)";
}

function send_headers(){
    global $headers;
    foreach($headers as $header){
        if(is_array($header)){
            header($header[0],$header[1]);
        }
        else{
            header($header);
        }
    }
}

function date_string($time = null) {
	if (is_null($time)) $time = time();
	return gmdate("D, d M Y H:i:s", $time) . " GMT";
}

function error($message){
    global $settings;
    if($settings['debug'] == TRUE){
        echo $message;
    }
    trigger_error($message, E_USER_ERROR);
    exit();
}

// Settings
$settings['base_dir'] = dirname(__FILE__).DIRECTORY_SEPARATOR;
$settings['cache_dir'] =  '../application/cache/';
$settings['charset'] =  'utf-8';
$settings['server_cache'] =  FALSE;
$settings['client_cache'] =  FALSE;
$settings['debug'] = FALSE;
$settings['embed'] =  FALSE;
$settings['embed_max_size'] =  5120;
$settings['gzip'] =  FALSE;
$settings['gzip_level'] =  6;
$settings['minify'] =  TRUE;
$settings['minifyJS'] =  TRUE;
$settings['separator'] =  ',';
$settings['valid_types'] =  array('css','js');

// Mime types
$mime_types = array(
	'js'	=> 'text/javascript',
	'css'	=> 'text/css',
);

// If no files specified just exit
if(empty($_SERVER['QUERY_STRING'])){
    error('No files specified.');
}

// Create an array of the files passed in
$files = explode($settings['separator'], $_SERVER['QUERY_STRING']);

// Get the file types from file namese
foreach($files as $file){
    $extension = pathinfo($file,PATHINFO_EXTENSION);
    $types[] = $extension;
}

if(count(array_unique($types)) > 1){
    error("Files must be of the same type.");
}

$extension = $types[0];

// Disable embedding if browser is IE, user agent not set, or file is js
if($extension == 'js' || empty($_SERVER['HTTP_USER_AGENT']) || !preg_match('/msie/i',$_SERVER['HTTP_USER_AGENT'])){
    $settings['embed'] = FALSE;
}

// Array to store headers to be sent
$headers = array();

// Type of files
if(in_array($types[0], $settings['valid_types']) == TRUE){
    $settings['type'] = $types[0];
}
else {
    error("Invalid type requested.");
}

// Set the base_dir based on the type param
$settings['base_dir'] = $settings['base_dir'].$settings['type'].DIRECTORY_SEPARATOR;

// Combine the files into one
$content = '';

foreach($files as $file){
    $file_content = file_get_contents(realpath($settings['base_dir'].$file));

    if($file_content == FALSE){
        error('File not found: '.$settings['base_dir'].$file);
    }
    else{
        $content .= $file_content;
    }
}

$headers[] = "Content-Type: $mime_types[$extension]; charset={$settings['charset']};";

// Minify CSS contents
if($settings['minify'] == TRUE && $extension == 'css'){
    $content = minify($content);
}

// Minify JS contents
if($settings['minifyJS'] == TRUE && $extension == 'js'){
    $content = minifyJs($content);
}

// Gzip contents if the setting is true, the browser accepts gzip encoding, and the gzencode function exists
if( $settings['gzip'] == TRUE &&
    in_array('gzip', explode(',' , $_SERVER['HTTP_ACCEPT_ENCODING'])) &&
	function_exists('gzencode')){
        $headers[] = "Content-Encoding: gzip";
        $content = gzencode($content,$settings['gzip_level']);
}

// Server caching
if($settings['server_cache'] == TRUE){
    // Variable used to determine if new cache file should be created
    $create_cache = 0;

    // Generate the file name for cached file
    $cache_file = $settings['cache_dir'].md5(implode('', $files)).'.'.$settings['type'];
    if($settings['gzip'] == TRUE){
        $cache_file .= '.gz';
    }

    if(file_exists($cache_file)){
        // Check if any of the files specified have been modified since the cache was created
        $cache_file_modified = filemtime($cache_file);

        foreach($files as $file){
            if(filemtime($settings['base_dir'].$file) > $cache_file_modified){
                $create_cache++;
            }
        }

        // Check if this file has been modifed since the cache was created and recreate if it has.
        if(filemtime(__FILE__) > $cache_file_modified){
            $create_cache++;
        }

        $cache_exists = TRUE;
    }
    else{
        // Cached file doesn't exist so create it.
        $create_cache = 1;
    }


    // Don't need to recreate cache, just use it
    if($create_cache == 0 && $cache_exists == TRUE){
        $headers[] = 'Content-Length: ' . filesize($cache_file);
        send_headers();
        readfile($cache_file);
        exit();
    }

    // Create the cache
    if($create_cache > 0 || $cache_exists == FALSE){
        $handle = fopen($cache_file,'w') or error('Can not create cache file.');
        fwrite($handle, $content);
        fclose($handle);
    }
}
// Client caching
if($settings['client_cache'] == TRUE){
    $headers[] = 'Expires: '.date_string(time() + 315360000);
	$headers[] = 'Cache-Control: max-age=315360000';
}
else{
    $headers[] = 'Expires: Mon, 26 Jul 1997 05:00:00 GMT';
	$headers[] = 'Last-Modified: '.date_string();
	$headers[] = 'Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0';
	$headers[] = 'Pragma: no-cache';
	$headers[] = 'Etag: '.microtime();
}

$headers[] = 'Content-Length: ' . strlen($content);
send_headers();
echo $content;
exit();
?>